#include <stdio.h>
#include <string.h>
#include "filestorage.h"

FileStorage::FileStorage(const char *filename, uint32_t capacity)
	: initialized_(false)
	, capacity_(capacity)
	, file_(NULL)
{
	filename_ = new char[strlen(filename) + 1];
	strcpy(filename_, filename);
}

FileStorage::~FileStorage() {
	deinit();
	delete [] filename_;
}

bool FileStorage::lock() {
	return mutex_.lock(1000);
}

void FileStorage::unlock() {
	mutex_.unlock();
}

bool FileStorage::read(uint32_t addr, void *data, uint16_t dataSize) {
	if (!init() || !checkBounds(addr, dataSize))
		return false;

	fseek(file_, addr, SEEK_SET);
	fread(data, 1, dataSize, file_);
	return true;
}

bool FileStorage::write(uint32_t addr, const void *data, uint16_t dataSize) {
	if (!init() || !checkBounds(addr, dataSize))
		return false;

	fseek(file_, addr, SEEK_SET);
	fwrite(data, 1, dataSize, file_);
	fflush(file_);
	return true;
}

// open file and check size
bool FileStorage::init() {
	if (initialized_)
		return true;

	if (openExisting() || createNew()) {
		initialized_ = true;
		return true;
	}

	return false;
}

void FileStorage::deinit() {
	fclose(file_);
}

bool FileStorage::checkBounds(uint32_t addr, uint16_t size) {
	bool check_bounds_res = (addr < capacity_) && (addr + size < capacity_);
	return check_bounds_res;
}

bool FileStorage::openExisting() {
	if ((file_ = fopen(filename_, "r+b")) == NULL)
		return false;

	fseek(file_, 0, SEEK_END);
	if (ftell(file_) != capacity_) {
		fclose(file_);
		return false;
	}

	return true;
}

bool FileStorage::createNew() {
	if ((file_ = fopen(filename_, "w+b")) == NULL)
		return false;

	uint8_t data[32] = {0};
	uint32_t pos = 0;
	while (pos < capacity_) {
		uint32_t writesize = sizeof(data);
		if (writesize > capacity_ - pos)
			writesize = capacity_ - pos;
		fwrite(data, 1, writesize, file_);
		pos += writesize;
	}

	fclose(file_);

	return (file_ = fopen(filename_, "r+b")) != NULL;
}
